<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>观察者模式  发布/订阅模式(PublishSubscribe)</title>
</head>
<body>
	
	
	<script>
		/**
		 * 观察者模式是软件设计模式的一种。
		 * 在此种模式中，一个目标对象管理所有相依于它的观察者对象，并且在它本身的状态改变时主动发出通知。
		 * 这通常透过呼叫各观察者所提供的方法来实现。
		 * 此种模式通常被用来实时事件处理系统
		 * 
		 * 假如我今天想订阅xxx公司的邮件，那么这里就涉及到两个对象：我和xxx公司，
		 * 从行为上来看就是我订阅了xxx公司邮件，xxx公司会发送邮件给我的邮箱。
		 * 但某天我不想再收到xxx公司的邮件了，那么我可以取消订阅，这样xxx公司就不会再发邮件到我的邮箱。
		 * 
		 * 对象：我(订阅者), xxx公司(发布者), 可以直接对应 发布/订阅模式(Publish/Subscribe)
		 * 行为：订阅、发送和取消订阅
		 * 
		 * 首先，我们订阅一个xxx网站的邮件，是不是需要xxx网站给我们提供订阅入口？那么publisher中必定会有一个方法提供给我们实现订阅。
		 * 其次，如果xxx公司要向订阅者们发送自己的邮件，是不是需要一个方法去做？那么publisher中必定会有一个方法提供给我们实现发送或者说说发布。
		 * 再然后，如例子所说如果我突然不想订阅xxx公司的邮件了，xxx公司 就得提供给我一个取消订阅的入口，那么publisher中必定会有一个方法提供给我们实现取消订阅。
		 * 最后，如果我们订阅了xxx公司的邮件，那么他就得记录我订阅所用的邮箱地址吧，所以publisher中必定会有一个“注册表”来存储订阅的对象，也就是说我们的 邮箱地址。
		 **/
		// 发布者 (相当于公司)
		let publisher = {
			//注册表
			registration: {},
			//订阅
			subscribe: function (type, fn) {
				if(Object.keys(this.registration).indexOf(type) >= 0){
					this.registration[type].push(fn);
				}else{
					this.registration[type] = [];
					this.registration[type].push(fn);
				}
			},
			//取消订阅
			unSubscribe: function (type, fnName) {
				if (Object.keys(this.registration).indexOf(type) >= 0) {
					let index = -1;
					this.registration[type].forEach(function (func, idx) {
						if (func.name === fnName) {
							index = idx;
						}
					})
					index > -1 ? this.registration[type].splice(index, 1) : null
				}
			},
			//向所有订阅者发布消息的方法
			//通过 type 找到指定数组，然后对数组中的回调函数进行依次调用，达到发布的目的
			publish: function (type, message) {
				if (Object.keys(this.registration).indexOf(type) >= 0) {
					for (let fn of this.registration[type]) {
						fn(message)
					}
				}
			} 
		}
		 
		//订阅者
		let subscriberA = function (message) {
			console.log(`A收到通知：${message}`)
		};

		let subscriberB = function (message) {
			console.log(`B收到通知：${message}`)
		};

		let subscriberC = function (message) {
			console.log(`C收到通知：${message}`)
		};

		publisher.subscribe('game', subscriberA);
		publisher.subscribe('game', subscriberB);
		publisher.subscribe('game', subscriberC);
		
		//取消订阅
		publisher.unSubscribe('game', subscriberB.name)
		

		publisher.publish('game', '恭喜RNG获得LOL 2018季中赛冠军！')
		
		
		
		
		
	</script>
	
</body>
</html>